package montecarlo.multi.thread;


import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import montecarlo.european.option.*;
import montecarlo.gui.MonteCarloGui;


public class MonteCarloMultiThread {
	//Declaration
    private int nSimulation;
    private double res;
    private double Sum;
    final int numberOfCores = Runtime.getRuntime().availableProcessors();
    final double blockingCoefficient = 0.9;
    final int poolSize = (int) (numberOfCores / (1 - blockingCoefficient));
    double time;

    //Constructor
    public MonteCarloMultiThread(final String CallPutFlag,final double S, final double X,
        final double T,	final double r,final double b,
        final double v, final int nSteps,final int nSimulations) {
        
    	//Start time
		final long start = System.nanoTime();
		int z = 1;
        Sum=0.0;
		this.nSimulation=nSimulations;
         //List of threads     
		final List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
	    		
		 for(int i=0;i<nSimulation;i++) 
		    {
			 	//We add a new thread to our list
	 			partitions.add(new Callable<Double>() 
		    	{
	 				//This method will be call for each thread
		    		 public Double call() throws Exception
		    		 {
		    			 //we compute the monte carlo function
		    			 return  MonteCarloStandardOption(CallPutFlag,S,X,T,r,b, v
		    						,nSteps,nSimulation);    			
		    		 }       
		    	});
		    }
					
		try {
		//We use the framework executor to work some reusable threads
    	final ExecutorService executorPool = Executors .newFixedThreadPool(poolSize);
    	//represent the future values of our threads
		final List<Future<Double>> values = executorPool.invokeAll(
				partitions, 10000, TimeUnit.SECONDS);
        //We calculate the sum by adding the value of each thread
		for(Future<Double> value : values){
                   
			Sum+= value.get();
                      
                       
		}
		//Shutdown the ExecutorService
		executorPool.shutdown();
		//end time
		final long end = System.nanoTime();
		//time taken
		time= (end - start)/1.0e9;
		
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (ExecutionException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}			
		
		res=Math.exp(-r*T)*(Sum/nSimulation);
    
    }
	//monte carlo function
	public Double MonteCarloStandardOption(String CallPutFlag,double St, double X,double T,
			double r,double b,double v,int nSteps, int nSimulations	) {
		
        Random rand = new Random() ;
		double dt = T / nSteps;
		double Drift = (b - (v*v) / 2) * dt;
		double vSqrdt = v * Math.sqrt(dt);
		int z=1;
                        
			if (CallPutFlag.equals("c")){
                           
				z = 1;
			}
						
			else if (CallPutFlag.equals("p")){
                           
				z=-1;
			}
	
			for(int j=1;j<=nSteps;j++)
			{
			
		    St= St * Math.exp(Drift + vSqrdt * rand.nextGaussian());
			
			}
			
			return Math.max(z*(St-X),0);	
				
		}
	
	public static ArrayList <EuropeanOption> readValues() throws IOException {
	    final BufferedReader reader = 
	      new BufferedReader(new FileReader("european-options.txt"));
	    
	    final ArrayList <EuropeanOption> options = new  ArrayList <EuropeanOption> ();
	    
	    String option = null;
	   
	    while((option = reader.readLine()) != null) {
	    	//We split a line 
	      final String[] values = option.split(",");
	      //we instantiate a new object EuropeanOption
	      EuropeanOption eo = new EuropeanOption(
	    		  values[0],Double.valueOf(values[1]),Double.valueOf(values[2]),Double.valueOf(values[3]),
	    		  Double.valueOf(values[4]),Double.valueOf(values[5]),Double.valueOf(values[6]),
	    		  Integer.valueOf(values[7]),Integer.valueOf(values[8]));
	
	      
	     options.add(eo); 
	    }
	    
	    return options;    
	  }
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			ArrayList <EuropeanOption> options = readValues();
			MonteCarloGui gui = new MonteCarloGui(options, "multi");
			gui.setVisible(true);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public int getNumberOfCores() {
		return numberOfCores;
	}

	public double getBlockingCoefficient() {
		return blockingCoefficient;
	}

	public int getPoolSize() {
		return poolSize;
	}

	public double getTime() {
		return time;
	}

	public double getRes() {
		return res;
	}




	
	

}
